package com.iwdnb.gkgz.application.strategy;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.iwdnb.gkgz.application.model.request.AddStrategyTradeStockDataRequest;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StrategyTradeDTO;
import com.iwdnb.gkgz.common.quota.CCIIndicator;
import com.iwdnb.gkgz.common.quota.MacdIndicator;
import com.iwdnb.gkgz.common.quota.MovingAverage;
import com.iwdnb.gkgz.common.quota.RSIIndicator;
import com.iwdnb.gkgz.common.quota.WRIndicator;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.DateUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import static com.iwdnb.gkgz.common.utils.StockUtils.getAveragePrice;

/**
 * macd叠加60日均线策略
 */
@Service
@Slf4j
public class Macd60StockBackValidateService extends CommmonStockBackValidateService {

    private static final String STATEGY_CODE = "macd60";

    /**
     * 高价日期后10天的毫秒数
     */
    private static final Long HIGH_DAY_PERIOD_MILLIONS_SECOND = 10 * 86400 * 1000L;
    private BigDecimal pointFive = new BigDecimal(0.5);
    private static final BigDecimal pointZeroFive = new BigDecimal(0.05);
    private static final BigDecimal fuFifty = new BigDecimal(-50);
    int sixtyPeriod = 60;

    @Override
    public List<StrategyTradeDTO> queryBackValidateDateList(String code, String strategy,
        List<StockDayData> stockDayDataList, AddStrategyTradeStockDataRequest request) {
        MacdIndicator.calculateMACD(stockDayDataList);
        RSIIndicator.calculateRSI(stockDayDataList);
        CCIIndicator.calculateCCI(stockDayDataList);
        WRIndicator.calculateWR(stockDayDataList);
        MovingAverage.calculateEMA(stockDayDataList, sixtyPeriod);
        List<String> dateList = new ArrayList<>();
        List<StrategyTradeDTO> strategyTradeDTOList = new ArrayList<>();
        for (int i = 0; i < stockDayDataList.size() - 1; i++) {
            if (i < 700) {
                continue;
            }
            StockDayData data = stockDayDataList.get(i);
            String date = data.getDate();
            BigDecimal closePrice = data.getClosePrice();
            BigDecimal sixtyAvg = getAveragePrice(data, sixtyPeriod);
            StockDayData beforeTwoData = stockDayDataList.get(i - 2);
            StockDayData beforeOneData = stockDayDataList.get(i - 1);
            //前一日股价下跌
            if (BigDecimalUtils.isGe(beforeOneData.getRate(), BigDecimal.ZERO)) {
                continue;
            }
            //当日股价企稳或上涨
            if (BigDecimalUtils.isLt(data.getRate(), BigDecimal.ZERO)) {
                continue;
            }
            //股价在60日线上
            if (BigDecimalUtils.isLe(closePrice, sixtyAvg)) {
                continue;
            }
            //List<StockDayData> beforeDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList, date, 20);
            //if (!upSixtyLine(beforeDatas)) {
            //    continue;
            //}
            ////macd快慢线大于0
            //if (BigDecimalUtils.isLt(data.getMacd().getDea(), BigDecimal.ZERO)
            //    && BigDecimalUtils.isLt(data.getMacd().getDif(), BigDecimal.ZERO)) {
            //    continue;
            //}
            ////macd量柱红翻绿再翻红
            //if (BigDecimalUtils.isLe(beforeTwoData.getMacd().getBar(), BigDecimal.ZERO)
            //    || BigDecimalUtils.isGe(beforeOneData.getMacd().getBar(), BigDecimal.ZERO)
            //    || BigDecimalUtils.isLt(data.getMacd().getBar(), BigDecimal.ZERO)
            //) {
            //    continue;
            //}
            //股价重新上穿60日线
            if (BigDecimalUtils.isGe(beforeOneData.getClosePrice(), getAveragePrice(beforeOneData, sixtyPeriod))
                || BigDecimalUtils.isLt(data.getClosePrice(), getAveragePrice(data, sixtyPeriod))
            ) {
                continue;
            }
            //股价与250天内最低价相差50%以内
            StockDayData yearData = StockUtils.getMinPriceDataBeforeDate(stockDayDataList, date, 250);
            BigDecimal yearLowPrice = yearData.getMinPrice();
            BigDecimal yearLowRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
                yearLowPrice);
            if (BigDecimalUtils.isGe(yearLowRate, fifty)) {
                continue;
            }
            //股价与250天内最高价相差50%以内
            BigDecimal highPrice = StockUtils.getMaxPriceBeforeDate(stockDayDataList, date, 250);
            BigDecimal highRate = BigDecimalUtils.subStractAndDividePrecent(highPrice, beforeTwoData.getClosePrice());
            if (BigDecimalUtils.isGe(highRate, fifty)) {
                continue;
            }
            //股价与40天内最低价相差30%以内
            StockDayData lowData = StockUtils.getMinPriceDataBeforeDate(stockDayDataList, date, 40);
            BigDecimal lowPrice = lowData.getMinPrice();
            BigDecimal lowRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(), lowPrice);
            if (BigDecimalUtils.isGe(lowRate, thirty)) {
                continue;
            }

            //最低价在15和35天之间
            int dateCount = StockUtils.getDateCount(stockDayDataList, lowData.getDate(), date);
            if (dateCount < 15 || dateCount > 35) {
                continue;
            }
            List<StockDayData> beforeTenDayDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList, date,
                10);
            StockDayData beforeTenData = beforeTenDayDatas.get(0);
            BigDecimal beforeTenAvg = getAveragePrice(beforeTenData, sixtyPeriod);
            BigDecimal beforeTenRate = BigDecimalUtils.subStractAndDividePrecent(sixtyAvg, beforeTenAvg);
            //10日前的60日均价比当天低5%以内
            if (BigDecimalUtils.isGt(beforeTenRate, new BigDecimal(5))) {
                continue;
            }
            //rsi值小于45
            BigDecimal rsi = StockUtils.getStockQuota(lowData, RSIIndicator.QUOTA_NAME);
            if (BigDecimalUtils.isGe(rsi, fourtyFive)) {
                continue;
            }
            //cci值小于-170
            BigDecimal cci = StockUtils.getStockQuota(lowData, CCIIndicator.QUOTA_NAME);
            if (BigDecimalUtils.isGe(cci, fuFifty)) {
                continue;
            }
            //wr小于70
            BigDecimal wr = StockUtils.getStockQuota(lowData, WRIndicator.QUOTA_NAME);
            if (BigDecimalUtils.isLe(wr, seventy)) {
                continue;
            }
            //前面40天出现过股价上穿60日线
            List<StockDayData> beforeDayDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList,
                date, 40);
            beforeDayDatas = beforeDayDatas.subList(5, 35);
            boolean downFlag = false;
            boolean crossFlag = false;
            for (StockDayData beforeData : beforeDayDatas) {
                BigDecimal beforePrice = beforeData.getClosePrice();
                BigDecimal beforeSixtyAvg = getAveragePrice(beforeData, sixtyPeriod);
                if (!downFlag && BigDecimalUtils.isLe(beforePrice, beforeSixtyAvg)) {
                    downFlag = true;
                }
                if (downFlag && BigDecimalUtils.isGt(beforePrice, beforeSixtyAvg)) {
                    crossFlag = true;
                    break;
                }
            }
            if (!crossFlag) {
                continue;
            }
            //beforeDayDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList, date, 20);
            ////60日均线向上
            //TrendTypeEnums sixtyTrendType = TrendTypeEnums.getTrendType(beforeDayDatas, data, sixtyPeriod);
            //if (sixtyTrendType.isDown()) {
            //    continue;
            //}
            String buyDate = data.getDate();
            if (!dateList.contains(buyDate)) {
                dateList.add(buyDate);
                StrategyTradeDTO strategyTradeDTO = new StrategyTradeDTO();
                strategyTradeDTO.setCode(code);
                strategyTradeDTO.setUuid(STATEGY_CODE);
                strategyTradeDTO.setBuyDate(DateUtil.parseDate(buyDate));
                strategyTradeDTO.setBuyPrice(data.getClosePrice());
                strategyTradeDTO.setBuyMinPrice(data.getMinPrice());
                strategyTradeDTO.setStatus("buy");
                BigDecimal limitSellPrice = data.getMinPrice();
                strategyTradeDTO.setLimitSellPrice(limitSellPrice);
                strategyTradeDTO.setSignalPrice(data.getClosePrice());
                strategyTradeDTO.setBuySignalRate(
                    BigDecimalUtils.subStractAndDividePrecent(strategyTradeDTO.getSignalPrice(),
                        strategyTradeDTO.getBuyPrice()));
                strategyTradeDTO.setBuySignalMinRate(
                    BigDecimalUtils.subStractAndDividePrecent(strategyTradeDTO.getSignalPrice(),
                        strategyTradeDTO.getBuyMinPrice()));
                strategyTradeDTO.setV1(rsi);
                strategyTradeDTO.setV2(cci);
                strategyTradeDTO.setV3(wr);
                strategyTradeDTO.setV4(beforeTwoData.getMacd().getBar());
                strategyTradeDTO.setV5(beforeOneData.getMacd().getBar());
                strategyTradeDTO.setV6(data.getMacd().getBar());
                strategyTradeDTO.setV7(yearLowPrice);
                strategyTradeDTO.setV8(lowPrice);

                strategyTradeDTOList.add(strategyTradeDTO);
                //BigDecimal profit = caculateAfterDaysProfit(backDayData.getDate(), stockDayDataList, 5);
                //log.debug("{}-{}买入,未来5天最高收益为:{}", code, buyDate, profit);
            }

        }
        return strategyTradeDTOList;
    }

    private boolean upSixtyLine(List<StockDayData> beforeDatas) {
        int downCount = 0;
        int upCount = 0;
        int upIndex = 0;
        for (int i = 0; i < beforeDatas.size(); i++) {
            StockDayData data = beforeDatas.get(i);
            if (BigDecimalUtils.isLt(data.getClosePrice(), getAveragePrice(data, sixtyPeriod))) {
                downCount++;
            } else if (downCount == 0) {
                //从第1天开始就在60日均线上,说明没有上穿动作
                return false;
            } else {
                if (upIndex == 0) {
                    upIndex = i;
                }
                upCount++;
            }
        }
        int size = beforeDatas.size() - upIndex;
        double percent = upCount / size * 1.0;
        return percent > 0.7;
    }

    private boolean isInHighDate(String dateStr, List<Date> highDateList) {
        Date date = DateUtil.parseDate(dateStr);
        for (Date highDate : highDateList) {
            if (date.before(highDate)) {
                continue;
            }
            if (date.getTime() - highDate.getTime() < HIGH_DAY_PERIOD_MILLIONS_SECOND) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void doBackValidate(String code, String strategy, List<StockDayData> stockDayDataList,
        List<StrategyTradeDTO> strategyTradeDTOList) {
        List<String> dateList = strategyTradeDTOList.stream().map(o -> DateUtil.formatDate(o.getBuyDate()))
            .collect(Collectors.toList());
        Date latestEndDate = strategyTradeDTOList.get(0).getBuyDate();
        latestEndDate = DateUtil.offsetDay(latestEndDate, -1);
        for (StrategyTradeDTO strategyTradeDTO : strategyTradeDTOList) {
            Date d = strategyTradeDTO.getBuyDate();
            if (DateUtils.beforeOrEquals(d, latestEndDate)) {
                log.debug("{}-{}在上一个周期内,忽略", code, d);
                continue;
            }
            getTradeInfo(code, stockDayDataList, strategyTradeDTO);
            latestEndDate = strategyTradeDTO.getSellDate();
        }
    }

    private void getTradeInfo(String code, List<StockDayData> stockDayDataList,
        StrategyTradeDTO strategyTradeDTO) {
        String date = DateUtil.formatDate(strategyTradeDTO.getBuyDate());
        //log.debug("{}-{}>>>>>股票信息:{}", code, date, strategyTradeDTO.getSummary());
        StockDayData buyDayData = StockUtils.getStockDayData(stockDayDataList, date);
        BigDecimal buyPrice = buyDayData.getClosePrice();
        List<StockDayData> subList = StockUtils.getStockDayDataListAfterDate(stockDayDataList, date, 100);
        if (CollectionUtils.isEmpty(subList) || subList.size() < 10) {
            return;
        }
        int size = subList.size();
        BigDecimal closeRate = null;
        String sellDate = null;
        String info = "";
        BigDecimal sellPrice = null;
        BigDecimal rate = closeRate;
        int holdIndex = 0;
        BigDecimal rangeMaxPrice = strategyTradeDTO.getBuyPrice();
        BigDecimal rangeMaxRate = BigDecimal.ZERO;
        //跌破13日均线累计次数
        int falldownCount = 0;
        for (int i = 0; i < 100; i++) {
            if (i == size) {
                return;
            }
            StockDayData data = subList.get(i);
            StockDayData yesterdayDayData = i == 0 ? data : subList.get(i - 1);
            BigDecimal closePrice = data.getClosePrice();
            closeRate = BigDecimalUtils.subStractAndDividePrecent(closePrice, buyPrice);
            sellDate = data.getDate();
            sellPrice = data.getClosePrice();
            holdIndex = i;
            BigDecimal upRate = BigDecimalUtils.subStractAndDividePrecent(data.getMaxPrice(), buyPrice);
            BigDecimal openRange = data.getOpenPrice().subtract(buyPrice);
            BigDecimal openRate = BigDecimalUtils.divideToPrecent(openRange, buyPrice);
            rangeMaxPrice = BigDecimalUtils.isGt(data.getMaxPrice(), rangeMaxPrice) ? data.getMaxPrice()
                : rangeMaxPrice;
            rangeMaxRate = BigDecimalUtils.subStractAndDividePrecent(rangeMaxPrice, buyPrice);
            BigDecimal realtimeRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(), buyPrice);
            rate = closeRate;
            String sellInfo = "收盘价卖出";

            // 跌破60日线，跑路
            if (BigDecimalUtils.isLt(data.getClosePrice(), StockUtils.getAveragePrice(data, sixtyPeriod))) {
                sellInfo = "跌破60日线,止损";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate,
                    closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            // 跌5个点，跑路
            if (BigDecimalUtils.isLt(closeRate, new BigDecimal(-5))) {
                sellInfo = "跌了超过5个点,止损";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate,
                    closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            //连跌5天，如果前面是涨停板就等跌破涨停板低点卖出
            if (i >= 4) {
                StockDayData beforeFourData = subList.get(i - 4);
                StockDayData beforeThreeData = subList.get(i - 3);
                StockDayData beforeTwoData = subList.get(i - 2);
                StockDayData beforeOneData = subList.get(i - 1);
                if (BigDecimalUtils.isLt(data.getRate(), BigDecimal.ZERO)
                    && BigDecimalUtils.isLt(beforeOneData.getRate(), BigDecimal.ZERO)
                    && BigDecimalUtils.isLt(beforeTwoData.getRate(), BigDecimal.ZERO)
                    && BigDecimalUtils.isLt(beforeThreeData.getRate(), BigDecimal.ZERO)
                    && BigDecimalUtils.isLt(beforeFourData.getRate(), BigDecimal.ZERO)) {
                    sellInfo = "连跌5天,止损";
                    log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo,
                        rate, openRate, closeRate, upRate);
                    info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                        + ",最高点收益=" + upRate;
                    break;
                }
            }
            //5天还没涨过5个点
            if (i == 10 && BigDecimalUtils.isLt(closeRate, five)) {
                sellInfo = "10天没涨过5个点,卖掉";
                log.debug("{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", code, data.getDate(), sellInfo, rate, openRate,
                    closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            if (BigDecimalUtils.isGe(realtimeRate, new BigDecimal(50))) {
                Integer yesterdayTradeNum = yesterdayDayData.getTradeNum() * 10;
                if (data.getTradeNum() >= yesterdayTradeNum) {
                    sellInfo = "出现十倍信号量,止盈";
                    log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo,
                        rate,
                        openRate,
                        closeRate, upRate);
                    info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                        + ",最高点收益=" + upRate;
                    break;
                }
            }

            //50个点以上，回落超过2成
            if (BigDecimalUtils.isGe(rangeMaxRate, new BigDecimal(50)) && BigDecimalUtils.isGe(
                rangeMaxRate.multiply(new BigDecimal(0.8)), realtimeRate)) {
                sellInfo = "回落2成,止盈";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate, closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            //20个点以上，回落到13个点
            if (BigDecimalUtils.isGe(rangeMaxRate, new BigDecimal(20))
                && BigDecimalUtils.isLe(realtimeRate.multiply(new BigDecimal(1.6)), rangeMaxRate)) {
                sellInfo = "20个点以上回落到13个点左右,止盈";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate, closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            //冲高回落
            if (BigDecimalUtils.isGe(rangeMaxRate, ten)
                && BigDecimalUtils.isLe(realtimeRate, two)) {
                sellInfo = "冲高回落,卖出";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate, closeRate, upRate);
                info += "第" + (i + 1) + "天-" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }

            if (i == 99) {
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate,
                    closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }

        }
        strategyTradeDTO.setHoldDay(holdIndex + 1);
        strategyTradeDTO.setSellDate(DateUtil.parseDate(sellDate));
        strategyTradeDTO.setSellPrice(sellPrice);
        strategyTradeDTO.setProfitRate(rate);
        strategyTradeDTO.setRangeMaxPrice(rangeMaxPrice);
        strategyTradeDTO.setRangeMaxRate(BigDecimalUtils.subStractAndDividePrecent(rangeMaxPrice, buyPrice));
        strategyTradeDTO.setStrategyDescription(info);
        strategyTradeDTO.setStatus("sell");
        strategyTradeDTO.setWinFlag(BigDecimalUtils.isGt(rate, BigDecimal.ZERO) ? "y" : "n");
    }

}
